﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;

namespace Borg.Framework
{
    public class ToolStripItemCollectionHandler
    {
        private readonly Dictionary<SlotInfo, PlugTypeHandler> _actionSlots = new Dictionary<SlotInfo, PlugTypeHandler>();
        private readonly Dictionary<SlotInfo, PlugTypeHandler> _createSlots = new Dictionary<SlotInfo, PlugTypeHandler>();

        private readonly ToolStripItemCollection _items;

        private readonly Dictionary<SlotInfo, PlugHandler> _plugSlots = new Dictionary<SlotInfo, PlugHandler>();
        private readonly Dictionary<SlotInfo, PlugTypeHandler> _registerSlots = new Dictionary<SlotInfo, PlugTypeHandler>();
        private readonly Dictionary<SlotInfo, PlugHandler> _selectSlots = new Dictionary<SlotInfo, PlugHandler>();

        private readonly Dictionary<ToolStripItem, MenuItem> _toolStripItems = new Dictionary<ToolStripItem, MenuItem>();

        public event MenuItemEventHandler AfterHandleClick;
        public event MenuItemEventHandler BeforeHandleClick;

        public ToolStripItemCollectionHandler(ToolStripItemCollection items)
        {
            _items = items;

            Runtime.Repository.PluginAdded += PluginAdded;
            Runtime.Repository.PluginRemoved += PluginRemoved;
        }

        private void ActionMenuItem_Click(object sender, EventArgs args)
        {
            var item = (ToolStripMenuItem)sender;
            var menu = _toolStripItems[item];
            var eventArgs = new MenuItemEventArgs((ToolStripMenuItem)sender, menu.Slot, menu.PlugType);
            OnBeforeHandleClick(sender, eventArgs);

            if (eventArgs.SuppressClick)
                return;

            var plug = GetAction(menu) ?? menu.PlugType.CreateExtension(menu.Slot);
            ((IAction)plug.Object).Do(sender, new ActionEventArgs(plug, menu.Slot));
            OnAfterHandleClick(sender, eventArgs);
        }

        private void ActionMenuItem_Deregistered(object sender, RegisterEventArgs args)
        {
            RemoveMenuItem(args.PlugTypeInfo, args.SlotInfo, MenuItemKind.Action);
        }

        private void ActionMenuItem_Registered(object sender, RegisterEventArgs args)
        {
            var menuItemString = _actionSlots[args.SlotInfo](args.PlugTypeInfo);
            AddActionMenuItem(menuItemString, args.SlotInfo, args.PlugTypeInfo);
        }

        private void AddActionMenuItem(string menuItemString, SlotInfo si, PlugTypeInfo pti)
        {
            var item = AddMenuItem(
                menuItemString, pti, null, si, MenuItemKind.Action) as ToolStripMenuItem;

            if (item != null)
                item.Click += ActionMenuItem_Click;
        }

        private void AddCreateMenuItem(string menuItemString, SlotInfo si, PlugTypeInfo pti)
        {
            var item = AddMenuItem(menuItemString, pti, null, si, MenuItemKind.Create) as ToolStripMenuItem;

            if (item == null)
                return;

            item.Click += CreateMenuItem_Click;

            foreach (var plug in si.PluggedPlugInfos)
            {
                if (plug.PlugTypeInfo != pti)
                    continue;

                item.Checked = true;
                break;
            }
        }

        private ToolStripItem AddMenuItem(string menuItemString, PlugTypeInfo pti, PlugInfo pi, SlotInfo si, MenuItemKind kind)
        {
            var dblPound = menuItemString.Split(new[] { "##" }, StringSplitOptions.None);
            var tilde = dblPound[0].Split(new[] { '~' });
            ToolStripItem key = null;
            MenuItem menuItem = null;
            var dropDownItems = _items;

            foreach (var pipe in tilde.Select(s => s.Split(new[] { '|' })))
            {
                key = dropDownItems.Cast<ToolStripItem>().FirstOrDefault(toolStripItem => toolStripItem.Text == pipe[0]);

                if (key == null || !_toolStripItems.ContainsKey(key) || !_toolStripItems[key].IsPlaceholder)
                {
                    float f;

                    if (!float.TryParse(pipe[1], NumberStyles.Float, CultureInfo.InvariantCulture, out f))
                    {
                        MessageBox.Show("Invalid float: {0}", pipe[1]);
                        return null;
                    }

                    var i = 0;

                    while (i < dropDownItems.Count &&
                           (!_toolStripItems.ContainsKey(dropDownItems[i]) ||
                            f >= _toolStripItems[dropDownItems[i]].Order))
                        i++;

                    if (pipe[0] == string.Empty)
                        key = new ToolStripSeparator();
                    else
                    {
                        var toolStripMenuItem = new ToolStripMenuItem(pipe[0]);
                        toolStripMenuItem.DropDownOpened += DropDownOpened;
                        key = toolStripMenuItem;
                    }

                    menuItem = new MenuItem(si, pti, pi, f, MenuItemKind.None);
                    dropDownItems.Insert(i, key);
                    _toolStripItems.Add(key, menuItem);
                }

                if (key is ToolStripMenuItem)
                    dropDownItems = ((ToolStripMenuItem) key).DropDownItems;
                else
                    break;
            }

            if (key != null && dblPound.Length > 1 && !string.IsNullOrEmpty(dblPound[1]))
            {
                var type = pti != null
                    ? pti.ExtensionTypeInfo.Type
                    : si.ExtensionInfo.ExtensionTypeInfo.Type;

                try
                {
                    key.Image = new Icon(type, dblPound[1]).ToBitmap();
                }
                catch
                {
                    try
                    {
                        key.Image = new Bitmap(type, dblPound[1]);
                    }
                    catch
                    {
                        var name = GetType().Assembly.GetManifestResourceNames().
                            FirstOrDefault(resName => resName.EndsWith("MissingIcon.ico"));

                        if (name != null)
                            using (var stream = GetType().Assembly.GetManifestResourceStream(name))
                                if (stream != null)
                                    key.Image = new Icon(stream).ToBitmap();
                    }
                }
            }

            if (menuItem != null)
            {
                menuItem.kind = kind;
                menuItem.IsPlaceholder = false;
            }

            return key;
        }

        private void AddOpenMenuItem(string menuItemString, SlotInfo si)
        {
            var menuItem = AddMenuItem(
                menuItemString, null, null, si, MenuItemKind.Open) as ToolStripMenuItem;

            if (menuItem == null)
                return;

            menuItem.Click += OpenMenuItem_Click;

            if (si.IsOpen)
                menuItem.Checked = true;
        }

        private void AddPlugMenuItem(string menuItemString, SlotInfo si, PlugInfo pi)
        {
            var menuItem = AddMenuItem(
                menuItemString, pi.PlugTypeInfo, pi, si, MenuItemKind.Plug) as ToolStripMenuItem;

            if (menuItem == null)
                return;

            menuItem.Click += PlugMenuItem_Click;

            foreach (var plug in si.PluggedPlugInfos)
            {
                if (plug != pi)
                    continue;

                menuItem.Checked = true;
                break;
            }
        }

        private void AddRegisterMenuItem(string menuItemString, SlotInfo si, PlugTypeInfo pti)
        {
            var menuItem = AddMenuItem(
                menuItemString, pti, null, si, MenuItemKind.Register) as ToolStripMenuItem;

            if (menuItem == null)
                return;

            menuItem.Click += RegisterMenuItem_Click;

            if (si.RegisteredPlugTypeInfos.Contains(pti))
                menuItem.Checked = true;
            else if (!si.IsOpen)
                menuItem.Enabled = false;
        }

        private void AddSelectMenuItem(string menuItemString, SlotInfo si, PlugInfo pi)
        {
            var menuItem = AddMenuItem(
                menuItemString, pi.PlugTypeInfo, pi, si, MenuItemKind.Select) as ToolStripMenuItem;

            if (menuItem == null)
                return;

            menuItem.Click += SelectMenuItem_Click;
            menuItem.Checked = si.SelectedPlugs.Contains(pi);
        }

        private void CreateMenuItem_Click(object sender, EventArgs args)
        {
            var toolStripMenuItem = (ToolStripMenuItem)sender;
            var menuItem = _toolStripItems[toolStripMenuItem];
            var eventArgs = new MenuItemEventArgs((ToolStripMenuItem)sender, menuItem.Slot, menuItem.PlugType);
            OnBeforeHandleClick(sender, eventArgs);

            if (eventArgs.SuppressClick)
                return;

            var menu = false;
            
            foreach (var plug in menuItem.Slot.PluggedPlugInfos.
                Where(plug => plug.PlugTypeInfo == menuItem.PlugType))
            {
                menuItem.Slot.Unplug(plug);
                menu = true;
                break;
            }

            if (!menu)
            {
                ExtensionInfo sharedExtension;
                var extensionTypeInfo = menuItem.PlugType.ExtensionTypeInfo;

                if (menuItem.Slot.Unique)
                    sharedExtension =
                        SlotInfoEx.FindUniqueExtension(menuItem.Slot, extensionTypeInfo)
                        ?? extensionTypeInfo.CreateUniqueExtension();
                else
                    sharedExtension = extensionTypeInfo.GetSharedExtension(true);

                if (sharedExtension != null)
                    menuItem.Slot.Plug(sharedExtension.PlugInfos[menuItem.Slot.Name]);
            }

            OnAfterHandleClick(sender, eventArgs);
        }

        private void CreateMenuItem_Deregistered(object sender, RegisterEventArgs args)
        {
            RemoveMenuItem(args.PlugTypeInfo, args.SlotInfo, MenuItemKind.Create);
        }

        private void CreateMenuItem_Plugged(object sender, PlugEventArgs args)
        {
            SetChecked(args.SlotInfo, args.PlugInfo.PlugTypeInfo, true, MenuItemKind.Create);
        }

        private void CreateMenuItem_Registered(object sender, RegisterEventArgs args)
        {
            var menuItemString = _createSlots[args.SlotInfo](args.PlugTypeInfo);
            AddCreateMenuItem(menuItemString, args.SlotInfo, args.PlugTypeInfo);
        }

        private void CreateMenuItem_Unplugged(object sender, PlugEventArgs args)
        {
            SetChecked(args.SlotInfo, args.PlugInfo.PlugTypeInfo, false, MenuItemKind.Create);
        }

        private void DropDownOpened(object sender, EventArgs e)
        {
            var toolStripMenuItem = (ToolStripMenuItem)sender;

            foreach (ToolStripItem toolStripItem in toolStripMenuItem.DropDownItems)
            {
                var menuItem = toolStripItem as ToolStripMenuItem;

                if (menuItem == null)
                    continue;

                var plugType = _toolStripItems[menuItem].PlugType;

                if (plugType == null || plugType.Name != "Borg.Action" || (bool) plugType.GetParamValue("LazyLoad"))
                    continue;

                var menu = _toolStripItems[menuItem];
                var plug = GetAction(menu) ?? menu.PlugType.CreateExtension(menu.Slot);

                menuItem.Enabled = ((IAction)plug.Object).IsEnabled(menuItem, new ActionEventArgs(plug, menu.Slot));
            }
        }

        private static PlugInfo GetAction(MenuItem menu)
        {
            return menu.Plug ?? menu.Slot.PluggedPlugInfos.
                FirstOrDefault(plug => plug.PlugTypeInfo == menu.PlugType);
        }

        protected void OnAfterHandleClick(object sender, MenuItemEventArgs args)
        {
            if (AfterHandleClick != null)
                AfterHandleClick(sender, args);
        }

        protected void OnBeforeHandleClick(object sender, MenuItemEventArgs args)
        {
            if (BeforeHandleClick != null)
                BeforeHandleClick(sender, args);
        }

        private void OpenMenuItem_Click(object sender, EventArgs args)
        {
            var toolStripMenuItem = (ToolStripMenuItem)sender;
            var menuItem = _toolStripItems[toolStripMenuItem];
            var eventArgs = new MenuItemEventArgs((ToolStripMenuItem)sender, menuItem.Slot);

            OnBeforeHandleClick(sender, eventArgs);

            if (eventArgs.SuppressClick)
                return;

            if (menuItem.Slot.IsOpen)
                menuItem.Slot.Close();
            else
                menuItem.Slot.Open();

            OnAfterHandleClick(sender, eventArgs);
        }

        private void OpenMenuItem_Closed(object sender, SlotEventArgs args)
        {
            SetChecked(args.SlotInfo, false, MenuItemKind.Open);
        }

        private void OpenMenuItem_Opened(object sender, SlotEventArgs args)
        {
            SetChecked(args.SlotInfo, true, MenuItemKind.Open);
        }

        private void PluginAdded(object sender, PluginEventArgs args)
        {
            foreach (var kvPair in _registerSlots)
            {
                foreach (var eti in args.PluginInfo.ExtensionTypeInfos)
                {
                    foreach (var pti in eti.PlugTypeInfos.Find(kvPair.Key.Name))
                    {
                        var menuItemString = kvPair.Value(pti);
                        AddRegisterMenuItem(menuItemString, kvPair.Key, pti);
                    }
                }
            }
        }

        private void PluginRemoved(object sender, PluginEventArgs args)
        {
            foreach (var kvPair in _registerSlots)
                foreach (var eti in args.PluginInfo.ExtensionTypeInfos)
                    foreach (var pti in eti.PlugTypeInfos.Find(kvPair.Key.Name))
                        RemoveMenuItem(pti, kvPair.Key, MenuItemKind.Register);
        }

        private void PlugMenuItem_Click(object sender, EventArgs args)
        {
            var toolStripMenuItem = (ToolStripMenuItem)sender;
            var menuItem = _toolStripItems[toolStripMenuItem];
            var eventArgs = new MenuItemEventArgs((ToolStripMenuItem)sender, menuItem.Slot, menuItem.Plug);
            OnBeforeHandleClick(sender, eventArgs);

            if (eventArgs.SuppressClick)
                return;

            if (!menuItem.Slot.PluggedPlugInfos.Contains(menuItem.Plug))
                menuItem.Slot.Plug(menuItem.Plug);

            OnAfterHandleClick(sender, eventArgs);
        }

        private void PlugMenuItem_Created(object sender, ExtensionEventArgs args)
        {
            foreach (var slot in _plugSlots.Keys)
            {
                foreach (var pti in args.ExtensionTypeInfo.PlugTypeInfos)
                {
                    if (!slot.RegisteredPlugTypeInfos.Contains(pti))
                        continue;

                    var pi = args.ExtensionInfo.PlugInfos[pti.Name];
                    var menuItemString = _plugSlots[slot](pi);

                    AddPlugMenuItem(menuItemString, slot, pi);
                }
            }
        }

        private void PlugMenuItem_Deregistered(object sender, RegisterEventArgs args)
        {
            foreach (var pi in args.PlugTypeInfo.ExtensionTypeInfo.ExtensionInfos.
                Select(ei => ei.PlugInfos[args.PlugTypeInfo.Name]))
                RemoveMenuItem(pi, args.SlotInfo, MenuItemKind.Plug);

            args.PlugTypeInfo.ExtensionTypeInfo.ExtensionCreated -= PlugMenuItem_Created;
            args.PlugTypeInfo.ExtensionTypeInfo.ExtensionReleased -= PlugMenuItem_Released;
        }

        private void PlugMenuItem_Plugged(object sender, PlugEventArgs args)
        {
            SetChecked(args.SlotInfo, args.PlugInfo, true, MenuItemKind.Plug);
        }

        private void PlugMenuItem_Registered(object sender, RegisterEventArgs args)
        {
            foreach (var ei in args.PlugTypeInfo.ExtensionTypeInfo.ExtensionInfos)
            {
                var pi = ei.PlugInfos[args.PlugTypeInfo.Name];
                var menuItemString = _plugSlots[args.SlotInfo](pi);

                AddPlugMenuItem(menuItemString, args.SlotInfo, pi);
            }

            args.PlugTypeInfo.ExtensionTypeInfo.ExtensionCreated += PlugMenuItem_Created;
            args.PlugTypeInfo.ExtensionTypeInfo.ExtensionReleased += PlugMenuItem_Released;
        }

        private void PlugMenuItem_Released(object sender, ExtensionEventArgs args)
        {
            foreach (var si in _plugSlots.Keys)
            {
                foreach (var pti in args.ExtensionTypeInfo.PlugTypeInfos)
                {
                    if (!si.RegisteredPlugTypeInfos.Contains(pti))
                        continue;

                    var pi = args.ExtensionInfo.PlugInfos[pti.Name];
                    RemoveMenuItem(pi, si, MenuItemKind.Plug);
                }
            }
        }

        private void PlugMenuItem_Unplugged(object sender, PlugEventArgs args)
        {
            SetChecked(args.SlotInfo, args.PlugInfo, false, MenuItemKind.Plug);
        }

        private void RegisterMenuItem_Click(object sender, EventArgs args)
        {
            var toolStripMenuItem = (ToolStripMenuItem)sender;
            var menuItem = _toolStripItems[toolStripMenuItem];
            var eventArgs = new MenuItemEventArgs((ToolStripMenuItem)sender, menuItem.Slot, menuItem.PlugType);
            OnBeforeHandleClick(sender, eventArgs);

            if (eventArgs.SuppressClick)
                return;

            if (!menuItem.Slot.RegisteredPlugTypeInfos.Contains(menuItem.PlugType))
                menuItem.Slot.Register(menuItem.PlugType);
            else
                menuItem.Slot.Deregister(menuItem.PlugType);

            OnAfterHandleClick(sender, eventArgs);
        }

        private void RegisterMenuitem_Closed(object sender, SlotEventArgs args)
        {
            SetEnabled(args.SlotInfo, false, MenuItemKind.Register);
        }

        private void RegisterMenuItem_Deregistered(object sender, RegisterEventArgs args)
        {
            SetChecked(args.SlotInfo, args.PlugTypeInfo, false, MenuItemKind.Register);
        }

        private void RegisterMenuItem_Opened(object sender, SlotEventArgs args)
        {
            SetEnabled(args.SlotInfo, true, MenuItemKind.Register);
        }

        private void RegisterMenuItem_Registered(object sender, RegisterEventArgs args)
        {
            SetChecked(args.SlotInfo, args.PlugTypeInfo, true, MenuItemKind.Register);
        }

        public void RemoveActionBehavior(SlotInfo si)
        {
            _actionSlots.Remove(si);
            
            si.Registered -= ActionMenuItem_Registered;
            si.Deregistered -= ActionMenuItem_Deregistered;
            
            RemoveMenuItems(si, MenuItemKind.Action);
        }

        public void RemoveCreateBehavior(SlotInfo si)
        {
            _createSlots.Remove(si);

            si.Registered -= CreateMenuItem_Registered;
            si.Deregistered -= CreateMenuItem_Deregistered;

            si.Plugged -= CreateMenuItem_Plugged;
            si.Unplugged -= CreateMenuItem_Unplugged;
            
            RemoveMenuItems(si, MenuItemKind.Create);
        }

        private void RemoveMenuItem(KeyValuePair<ToolStripItem, MenuItem> pair)
        {
            var key = pair.Key;

            if (key == null)
                return;

            if (key is ToolStripMenuItem)
            {
                var toolStripMenuItem = (ToolStripMenuItem)key;

                switch (pair.Value.kind)
                {
                    case MenuItemKind.Open:
                        toolStripMenuItem.Click -= OpenMenuItem_Click;
                        break;

                    case MenuItemKind.Action:
                        toolStripMenuItem.Click -= ActionMenuItem_Click;
                        break;

                    case MenuItemKind.Register:
                        toolStripMenuItem.Click -= RegisterMenuItem_Click;
                        break;

                    case MenuItemKind.Create:
                        toolStripMenuItem.Click -= CreateMenuItem_Click;
                        break;

                    case MenuItemKind.Plug:
                        toolStripMenuItem.Click -= PlugMenuItem_Click;
                        break;
                }

                if (toolStripMenuItem.DropDownItems.Count > 0)
                {
                    _toolStripItems[toolStripMenuItem].IsPlaceholder = true;
                    return;
                }
            }

            var ownerItem = key.OwnerItem as ToolStripMenuItem;

            if (ownerItem != null)
                ownerItem.DropDownItems.Remove(key);
            else
                _items.Remove(key);

            _toolStripItems.Remove(key);
            
            while (ownerItem != null && ownerItem.DropDownItems.Count == 0 && _toolStripItems[ownerItem].IsPlaceholder)
            {
                var menuItem = ownerItem;

                ownerItem = (ToolStripMenuItem)ownerItem.OwnerItem;

                if (ownerItem != null)
                    ownerItem.DropDownItems.Remove(menuItem);
                else
                    _items.Remove(menuItem);
            }
        }

        private void RemoveMenuItem(PlugInfo pi, SlotInfo si, MenuItemKind kind)
        {
            foreach (var kvPair in _toolStripItems.
                Where(kvPair => kvPair.Value.Plug == pi &&
                    kvPair.Value.Slot == si &&
                    kvPair.Value.kind == kind))
            {
                RemoveMenuItem(kvPair);
                break;
            }
        }

        private void RemoveMenuItem(PlugTypeInfo pti, SlotInfo si, MenuItemKind kind)
        {
            foreach (var kvPair in _toolStripItems.
                Where(kvPair => kvPair.Value.PlugType == pti &&
                    kvPair.Value.Slot == si &&
                    kvPair.Value.kind == kind))
            {
                RemoveMenuItem(kvPair);
                break;
            }
        }

        private void RemoveMenuItems(SlotInfo si, MenuItemKind kind)
        {
            var kvPairs = new List<KeyValuePair<ToolStripItem, MenuItem>>(_toolStripItems.Count);

            kvPairs.AddRange(_toolStripItems.
                Where(kvPair => kvPair.Value.Slot == si && kvPair.Value.kind == kind));

            foreach (var kvPair in kvPairs)
                RemoveMenuItem(kvPair);
        }

        public void RemoveOpenBehavior(SlotInfo si)
        {
            si.Opened -= OpenMenuItem_Opened;
            si.Closed -= OpenMenuItem_Closed;
            
            RemoveMenuItems(si, MenuItemKind.Open);
        }

        public void RemovePlugBehavior(SlotInfo si)
        {
            _plugSlots.Remove(si);

            si.Registered -= PlugMenuItem_Registered;
            si.Deregistered -= PlugMenuItem_Deregistered;
            
            si.Plugged -= PlugMenuItem_Plugged;
            si.Unplugged -= PlugMenuItem_Unplugged;
            
            RemoveMenuItems(si, MenuItemKind.Plug);
        }

        public void RemoveRegisterBehavior(SlotInfo si)
        {
            _registerSlots.Remove(si);

            si.Registered -= RegisterMenuItem_Registered;
            si.Deregistered -= RegisterMenuItem_Deregistered;
            
            si.Opened -= RegisterMenuItem_Opened;
            si.Closed -= RegisterMenuitem_Closed;
            
            RemoveMenuItems(si, MenuItemKind.Register);
        }

        public void RemoveSelectBehavior(SlotInfo si)
        {
            _selectSlots.Remove(si);

            si.Plugged -= SelectMenuItem_Plugged;
            si.Unplugged -= SelectMenuItem_Unplugged;
            si.SelectionChanged -= SelectMenuItem_SelectionChanged;
            
            RemoveMenuItems(si, MenuItemKind.Select);
        }

        private void SelectMenuItem_Click(object sender, EventArgs e)
        {
            var toolStripMenuItem = (ToolStripMenuItem)sender;
            var menuItem = _toolStripItems[toolStripMenuItem];
            var eventArgs = new MenuItemEventArgs((ToolStripMenuItem)sender, menuItem.Slot, menuItem.Plug);
            OnBeforeHandleClick(sender, eventArgs);

            if (eventArgs.SuppressClick)
                return;

            if (menuItem.Slot.SelectedPlugs.Contains(menuItem.Plug))
            {
                if (menuItem.Slot.SelectionMode == SelectionMode.Multiple)
                    menuItem.Slot.DeselectPlugs(new[] {menuItem.Plug});
            }
            else
                menuItem.Slot.SelectPlugs(new[] {menuItem.Plug});

            OnAfterHandleClick(sender, eventArgs);
        }

        private void SelectMenuItem_Plugged(object sender, PlugEventArgs args)
        {
            var menuItemString = _selectSlots[args.SlotInfo](args.PlugInfo);
            AddSelectMenuItem(menuItemString, args.SlotInfo, args.PlugInfo);
        }

        private void SelectMenuItem_SelectionChanged(object sender, SelectionChangedEventArgs args)
        {
            foreach (var plug in args.RemovedPlugs)
                SetChecked(args.SlotInfo, plug, false, MenuItemKind.Select);

            foreach (var plug in args.AddedPlugs)
                SetChecked(args.SlotInfo, plug, true, MenuItemKind.Select);
        }

        private void SelectMenuItem_Unplugged(object sender, PlugEventArgs args)
        {
            RemoveMenuItem(args.PlugInfo, args.SlotInfo, MenuItemKind.Select);
        }

        public void SetActionBehavior(SlotInfo si, PlugTypeHandler itemHandler)
        {
            if (si == null || itemHandler == null || _actionSlots.ContainsKey(si))
                return;

            _actionSlots.Add(si, itemHandler);

            si.Registered += ActionMenuItem_Registered;
            si.Deregistered += ActionMenuItem_Deregistered;

            foreach (var pti in si.RegisteredPlugTypeInfos)
            {
                var menuItemString = itemHandler(pti);
                AddActionMenuItem(menuItemString, si, pti);
            }
        }

        private void SetChecked(SlotInfo si, bool value, MenuItemKind kind)
        {
            foreach (var kvPair in _toolStripItems.
                Where(kvPair => kvPair.Value.Slot == si && kvPair.Value.kind == kind))
            {
                ((ToolStripMenuItem)kvPair.Key).Checked = value;
                break;
            }
        }

        private void SetChecked(SlotInfo si, PlugInfo pi, bool value, MenuItemKind kind)
        {
            foreach (var kvPair in _toolStripItems.
                Where(kvPair => kvPair.Value.Plug == pi &&
                    kvPair.Value.Slot == si && kvPair.Value.kind == kind))
            {
                ((ToolStripMenuItem)kvPair.Key).Checked = value;
                break;
            }
        }

        private void SetChecked(SlotInfo si, PlugTypeInfo pti, bool value, MenuItemKind kind)
        {
            foreach (var kvPair in _toolStripItems.
                Where(kvPair => kvPair.Value.PlugType == pti &&
                                kvPair.Value.Slot == si && kvPair.Value.kind == kind))
            {
                ((ToolStripMenuItem)kvPair.Key).Checked = value;
                break;
            }
        }

        public void SetCreateBehavior(SlotInfo si, PlugTypeHandler itemHandler)
        {
            if (si == null || itemHandler == null || _createSlots.ContainsKey(si))
                return;

            _createSlots.Add(si, itemHandler);

            si.Registered += CreateMenuItem_Registered;
            si.Deregistered += CreateMenuItem_Deregistered;

            si.Plugged += CreateMenuItem_Plugged;
            si.Unplugged += CreateMenuItem_Unplugged;
            
            foreach (var pti in si.RegisteredPlugTypeInfos)
            {
                var menuItemString = itemHandler(pti);
                AddCreateMenuItem(menuItemString, si, pti);
            }
        }

        private void SetEnabled(SlotInfo si, bool value, MenuItemKind kind)
        {
            foreach (var kvPair in _toolStripItems.
                Where(kvPair => kvPair.Value.Slot == si &&
                    kvPair.Value.kind == kind))
                kvPair.Key.Enabled = value;
        }

        public void SetOpenBehavior(SlotInfo si, string itemPath)
        {
            if ((si != null) && (itemPath != null))
            {
                si.Opened += OpenMenuItem_Opened;
                si.Closed += OpenMenuItem_Closed;
                
                AddOpenMenuItem(itemPath, si);
            }
        }

        public void SetPlugBehavior(SlotInfo si, PlugHandler itemHandler)
        {
            if (si == null || itemHandler == null || _plugSlots.ContainsKey(si))
                return;

            _plugSlots.Add(si, itemHandler);

            si.Registered += PlugMenuItem_Registered;
            si.Deregistered += PlugMenuItem_Deregistered;
            si.Plugged += PlugMenuItem_Plugged;
            si.Unplugged += PlugMenuItem_Unplugged;

            foreach (var pti in si.RegisteredPlugTypeInfos)
            {
                foreach (var ei in pti.ExtensionTypeInfo.ExtensionInfos)
                {
                    var pi = ei.PlugInfos[pti.Name];
                    var menuItemString = itemHandler(pi);
                    
                    AddPlugMenuItem(menuItemString, si, pi);
                }

                pti.ExtensionTypeInfo.ExtensionCreated += PlugMenuItem_Created;
                pti.ExtensionTypeInfo.ExtensionReleased += PlugMenuItem_Released;
            }
        }

        public void SetRegisterBehavior(SlotInfo si, PlugTypeHandler itemHandler)
        {
            if (si == null || itemHandler == null || _registerSlots.ContainsKey(si))
                return;

            _registerSlots.Add(si, itemHandler);

            si.Registered += RegisterMenuItem_Registered;
            si.Deregistered += RegisterMenuItem_Deregistered;
            si.Opened += RegisterMenuItem_Opened;
            si.Closed += RegisterMenuitem_Closed;

            foreach (var pti in Runtime.Repository.PlugTypeInfos.Find(si.Name))
            {
                var menuItemString = itemHandler(pti);
                AddRegisterMenuItem(menuItemString, si, pti);
            }
        }

        public void SetSelectBehavior(SlotInfo si, PlugHandler itemHandler)
        {
            if (si == null || itemHandler == null)
                throw new ArgumentException();

            if (_selectSlots.ContainsKey(si))
                return;

            _selectSlots.Add(si, itemHandler);

            si.Plugged += SelectMenuItem_Plugged;
            si.Unplugged += SelectMenuItem_Unplugged;
            si.SelectionChanged += SelectMenuItem_SelectionChanged;

            foreach (var plug in si.PluggedPlugInfos)
            {
                var menuItemString = itemHandler(plug);
                AddSelectMenuItem(menuItemString, si, plug);
            }
        }
    }
}

